package com.group.service;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.group.dao.CoinDao;
import com.group.dao.GeneralBeanDao;
import com.group.model.CoinChargeInfo;
import com.group.util.CoinRandomDecimal;
import com.group.util.CoinTransferUtil;
import com.group.util.WeiXinUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

/**
 * 数字货币转账监控服务
 *
 * @author Zhangyang Hu
 */
@Service
public class CoinTransferService {

    private static final Logger logger = LoggerFactory.getLogger(CoinTransferService.class);

    private static final String ETHERSCAN_API_KEY = "FCQY2MBCQ6AQGB4AFZGD8GG247VWS63EME";

    private static final String WECC_CONTRACT_ADDRESS = "0xcc74cb21eba98fda21da9a75ef0c1fa26c9b03c4";

//    private static final String WECC_ADDRESS = "0xa74432956a98f07e13143826c1de3cc745f10bb6";
    private static final String WECC_ADDRESS = "0x10f1fbf304d507abde10b16a2209a5dfd0a1a2e9";
    // 10 ^ 18
    private static final BigDecimal WECC_TOKEN_DECIMAL = new BigDecimal("1000000000000000000");

    /**
     * wecc的确认块数
     */
    private static final Long WECC_CONFITMED_BLOCK_COUNT = 20L;

    @Autowired
    GeneralBeanDao beanDao;

    @Autowired
    CoinDao coinDao;

    @Autowired
    TickerService tickerService;

    /**
     * wecc转账过程监控
     */
    @Scheduled(initialDelay = 10000, fixedDelay = 1000 * 60 * 5)
    public void weccTransferMonitor() {

        if(CoinTransferUtil.firstRun) {
            CoinTransferUtil.firstRun = false;
            this.firstRunAction();
        }

        //先处理过期的wecc待充值记录
        //过期时间暂定为一小时
        long timestampDeadline = System.currentTimeMillis() - (3600 * 1000L);

        this.clearExpired(timestampDeadline);
        coinDao.updateCoinChargeInfo("wecc", timestampDeadline);

        CoinChargeInfo info = new CoinChargeInfo();
        info.setCid("wecc");
        info.setStatus("N");

        List<CoinChargeInfo> infoList = beanDao.queryList(info);

        if(infoList != null) {

            if( ! infoList.isEmpty()) {

                //首先调用etherscan.io的API来获取最近的交易记录
                String url = this.getEtherscanERC20TokenTransactionQueryUrl(WECC_CONTRACT_ADDRESS, WECC_ADDRESS, ETHERSCAN_API_KEY,
                        0, 100, "desc");

                HashSet<String> set = new HashSet<>();
                HashMap<String, Double> amountMap = new HashMap<>();

                try {
                    String jsonStr = WeiXinUtils.get(url);
                    JSONObject jsonObj = JSON.parseObject(jsonStr);
                    if(jsonObj.getString("message").equals("OK")) {

                        JSONArray jsonArray = jsonObj.getJSONArray("result");
                        int sz = jsonArray.size();

                        for(int i = 0; i < sz; ++i) {

                            JSONObject obj = jsonArray.getJSONObject(i);
                            long txTime = Long.valueOf( obj.getString("timeStamp") + "000" );
                            if(txTime >= timestampDeadline) {
                                //符合时间预期
                                //是对应地址的入账交易
                                if(obj.getString("to").equals(WECC_ADDRESS) &&
                                   obj.getLong("confirmations") >= WECC_CONFITMED_BLOCK_COUNT) {
                                    //获取value值
                                    BigDecimal valueBig = new BigDecimal( obj.getString("value") );
                                    BigDecimal realValue = valueBig.divide(WECC_TOKEN_DECIMAL);

                                    String valueStr = realValue.toString();
                                    if(valueStr.indexOf(".") > 0) {
                                        String sub = valueStr.substring( valueStr.indexOf(".") + 1 );
                                        if(sub.length() == 2) {
                                            set.add("0." + sub);
                                            amountMap.put("0." + sub, realValue.doubleValue());
                                            logger.info("Added {}", "0." + sub);
                                        }
                                    }
                                }
                            }else {
                                break;
                            }
                        }
                    }else {
                        logger.error("Invalid json returned. JSON is {}", jsonStr);
                        return;
                    }
                }catch (Exception ex) {
                    logger.info("Exception in weccTransferMonitor, Msg: {}", ex.getMessage());
                    return;
                }

                for (CoinChargeInfo infoRec : infoList) {

                    String key = infoRec.getRandomPrec() + "";
                    if(set.contains( key )) {

                        try {
                            infoRec.setStatus("S");
                            beanDao.save(infoRec);

                            //在我们的系统中生成相关的记录
                            tickerService.addTicker(infoRec.getUserId(), "wecc", amountMap.get(key), "直接转币");
                            //reuse random decimal
                            CoinRandomDecimal decTmp = new CoinRandomDecimal();
                            decTmp.randomDecimal = key;
                            int ind = Collections.binarySearch(CoinTransferUtil.randomList, decTmp);

                            if(ind >= 0) {
                                CoinTransferUtil.randomList.get(ind).available = true;
                            }

                        }catch (Exception ee) {
                            logger.error("Error! 用户转币记录可能不一致，请检查相关的表中数据。");
                            logger.error("Message: {}", ee.getMessage());
                            ee.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    private String getEtherscanERC20TokenTransactionQueryUrl(String contractAddr, String addr, String apiKey,
                                                             Integer pageNo, Integer offset, String orderBySeq) {

        // https://etherscan.io/apis#accounts
        return "https://api.etherscan.io/api?module=account&action=tokentx&contractaddress=" +
                contractAddr + "&address=" + addr + "&page=" + pageNo + "&offset=" + offset +
                "&sort=" + orderBySeq + "&apikey=" + apiKey;
    }

    private void firstRunAction() {

        CoinChargeInfo info = new CoinChargeInfo();
        info.setStatus("N");

        List<CoinChargeInfo> infoList = beanDao.queryList(info);

        logger.info("First run action in service...");

        if(infoList != null) {

            for(CoinChargeInfo rec : infoList) {

                String key = rec.getRandomPrec() + "";

                CoinRandomDecimal decTmp = new CoinRandomDecimal();
                decTmp.randomDecimal = key;
                int ind = Collections.binarySearch(CoinTransferUtil.randomList, decTmp);

                if(ind >= 0) {
                    CoinTransferUtil.randomList.get(ind).available = false;
                }
            }
        }
    }

    private void clearExpired(Long timestamp) {

        List<CoinChargeInfo> infoList = coinDao.queryExpired(timestamp);

        logger.info("Clear expired action...");

        if(infoList != null) {

            for(CoinChargeInfo rec : infoList) {

                String key = rec.getRandomPrec() + "";

                CoinRandomDecimal decTmp = new CoinRandomDecimal();
                decTmp.randomDecimal = key;
                int ind = Collections.binarySearch(CoinTransferUtil.randomList, decTmp);

                if(ind >= 0) {
                    CoinTransferUtil.randomList.get(ind).available = true;
                }
            }
        }
    }
}
